import * as fs from 'fs';
import * as path from 'path';
import * as crypto from 'crypto';
import { CompressMgr } from './compressMgr';
import { KeeperService } from './Keeper';
import { getConfig } from './config';

/**
 * 需要压缩的图片
 */
export class TinyUnit {
    mgr: CompressMgr;
    dir: string;
    dst: string;
    name: string;
    buffer: Buffer;
    md5: string;
    tinyMd5: string;

    bgtime: number = Date.now();

    constructor(dir: string, dstdDir: string, name: string, mgr: CompressMgr) {
        this.dir = dir;
        this.name = name;
        this.mgr = mgr;
        this.dst = dstdDir;

        this.mgr.addFile();

        this.loadFile();
    }

    loadFile() {
        fs.readFile(path.join(this.dir, this.name), ((err: NodeJS.ErrnoException, data: Buffer) => {
            if (err) {
                return;
            }
            else {
                this.buffer = data;
                var md5 = crypto.createHash('md5');
                this.md5 = md5.update(this.buffer).digest('hex');
                this.checkDBPic();
            }
        }).bind(this));
    }

    async checkDBPic() {
        let value = await KeeperService.getPic({ "$or": [{ "srcmd5": this.md5 }, { "tinyMd5": this.md5 }] });
        if (value) {
            if (value.pic && value.tinyMd5) {
                // 表示图片存在
                if (this.name != value.name) value.save('name', this.name, 'time', Date.now());
                if (!this.saveTinyPic(value.pic, value.tinyMd5, true)) {
                    value.removeSelf();
                }
            }
            else {
                this.mgr.addPool(this);
                value.removeSelf();
            }
        }
        else {
            // 表述图片不存在，需要保存
            this.mgr.addPool(this);
        }

    }

    async doTinySave(data: Buffer, succ: boolean) {
        if (data == null) {
            console.error('no data ' + this.dir + this.name);
            return;
        }
        data = Buffer.from(data);
        this.buffer = data;
        var md5 = crypto.createHash('md5');
        this.tinyMd5 = md5.update(this.buffer).digest('hex');
        if (!this.saveTinyPic(this.buffer, this.tinyMd5, succ)) {
            return;
        }
        if (!succ) return;
        var picDB = await KeeperService.getPic({ srcmd5: this.md5 });
        if (!picDB) {
            await KeeperService.insert({ srcmd5: this.md5, tinyMd5: this.tinyMd5, pic: this.buffer, time: Date.now(), name: this.name })
        }
        else {
            await picDB.save('tinyMd5', this.tinyMd5, 'pic', this.buffer, 'time', Date.now(), 'name', this.name);
        }
    }

    quickjudge(data: Buffer) {
        let type: string = "";
        let b0 = String.fromCharCode(data[0]);
        let b1 = String.fromCharCode(data[1]);
        let b2 = String.fromCharCode(data[2]);
        let b3 = String.fromCharCode(data[3]);
        let b6 = String.fromCharCode(data[6]);
        let b7 = String.fromCharCode(data[7]);
        let b8 = String.fromCharCode(data[8]);
        let b9 = String.fromCharCode(data[9]);
        // GIF 
        if (b0 == 'G' && b1 == 'I' && b2 == 'F')
            type = "GIF";
        // PNG 
        else if (b1 == 'P' && b2 == 'N' && b3 == 'G')
            type = "PNG";
        // JPG 
        else if (b6 == 'J' && b7 == 'F' && b8 == 'I' && b9 == 'F')
            type = "JPG";
        else if (b6 == 'E' && b7 == 'x' && b8 == 'i' && b9 == 'f')
            type = "JPG";
        else
            type = "Unknown";

        return type.toLocaleLowerCase();
    }

    isJPEG(data: Buffer) {
        if (!data) return false;
        if (this.quickjudge(data) != 'jpg') {
            return false;
        }

        return true;
    }

    isJPEGComplete(data: Buffer) {
        var checkL = [0xFF, 0xD9];
        for (var i = 0; i < checkL.length; i++) {
            var k = data.readInt8(data.length - checkL.length + i);
            if (k != checkL[i] && k + 256 != checkL[i]) {
                return false;
            }
        }

        return true;
    }

    isPng(data: Buffer) {
        if (!data) return false;
        if (this.quickjudge(data) != 'png') {
            return false;
        }

        var checkA = [137, 80, 78, 71, 13, 10, 26, 10];
        for (var i = 0; i < checkA.length; i++) {
            var k = data.readInt8(i);
            if (k != checkA[i] && k + 256 != checkA[i]) {
                return false;
            }
        }

        return true;
    }

    isComplete(data: Buffer) {
        var checkL = [0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130];
        for (var i = 0; i < checkL.length; i++) {
            var k = data.readInt8(data.length - checkL.length + i);
            if (k != checkL[i] && k + 256 != checkL[i]) {
                return false;
            }
        }

        return true;
    }

    saveTinyPic(data: Buffer, tinyMd5: string, succ: boolean) {
        if (!this.isPng(data) && !this.isJPEG(data)) {
            // this.mgr.addPool(this);
            // 不是目标用户，这里只能直接拷贝过来了
            console.error('not png:' + this.name);
            fs.writeFile(path.join(this.dst, this.name), data, ((e) => {
                e && console.error(e);
                this.mgr.finishFile(this.name, succ);
            }).bind(this));
            return false;
        }
        else if (this.isPng(data) && !this.isComplete(data)) {
            this.mgr.addPool(this);
            console.error('not full', this.name);
            return false;
        }
        else {
            fs.writeFile(path.join(this.dst, this.name), data, ((e) => {
                e && console.error(e);
                this.mgr.finishFile(this.name, succ);
            }).bind(this));
        }
        return true;
    }
}

/**
 * 一个需要转换的文件夹
 */
export class DirectoryUnit {
    mgr: CompressMgr;
    dir: string;
    dst: string;
    files: Array<string> = [];
    childs: Array<DirectoryUnit> = [];
    // parent: DirectoryUnit;
    constructor(dir: string, dst: string, mgr: CompressMgr) {
        this.mgr = mgr;
        this.dst = dst;
        this.dir = dir;
        if (!fs.existsSync(this.dst)) fs.mkdirSync(this.dst, { recursive: true });
        // this.mkdirsSync(this.dst);
        this.syncFiles();
    }

    /**
     * 遍历所有文件
     */
    syncFiles() {
        let excExts = [".png"];

        if (getConfig("jpgopen")) {
            excExts.push('.jpg');
            excExts.push('.jpeg');
        }

        var files = fs.readdirSync(this.dir);//, ((err: NodeJS.ErrnoException, files: Array<string>) => {
        for (var i = 0; i < files.length; i++) {
            var fileName = files[i];
            if (fileName == '..' || fileName == '.' || fileName == '__tinypng__') continue;
            var st = fs.statSync(path.join(this.dir, fileName));
            if (st.isDirectory()) {
                this.childs.push(new DirectoryUnit(path.join(this.dir, fileName), path.join(this.dst, fileName), this.mgr));
            }
            else {
                var rt = path.parse(fileName);
                if (excExts.indexOf(rt.ext) < 0) continue;
                this.files.push(fileName);
            }
        }
        this.doTinyPic();
        // }).bind(this));
    }

    /**
     * 压缩所有图片
     */
    doTinyPic() {
        for (var i = 0; i < this.files.length; i++) {
            this.doOnePic(this.dir, this.files[i]);
        }
    }

    /**
     * 执行一个压缩图片
     */
    protected doOnePic(dir: string, name: string) {
        // 这里先检查一下这个图片是否有缓存过
        new TinyUnit(dir, this.dst, name, this.mgr);
    }
}